iT邦幫忙

2022 iThome 鐵人賽

DAY 28
0
Mobile Development

新手向Android&Kotlin學習紀錄30天系列 第 28

第28天 認識Activity、Toast、Snackbar

  • 分享至 

  • xImage
  •  

簡介

今天終於回到Android Studio了,首先新建一個專案時,第一個一定會讓你選擇的就是要使用哪一款Activity做為開局啊?

所謂Activity是一種包含用戶介面的組件,主要用於和用戶互動。一個應用程式可以有0到多個Activity,但只少都會有一個,應該...沒有人會希望App不被看見吧0.0

本次選擇Empty Activity新建一個專案,會看到MainActivity.kt的檔案已經開啟,如果沒有的話它在java資料夾,點開後應該是會看到這樣:

用之前學到的kotlin語言知識,觀察一下會知道MainActivity是一個class,繼承AppCompatActivity類別,裡面IDE已經先幫我們覆寫了onCreate()函數,因為每個Activity都需要覆寫它,目前只是簡單的調用父類別的onCreate()方法,開始後在這裡加入很多我們需要的邏輯。

接著,下面還有一行setContentView(R.layout.activity_main)是用來加載佈局用的函數,裡面的引數activity_main就是我們要加載的佈局名稱,前面的R.layout,R是一個類別,繼承Any類別,先理解成資源總管的感覺,layout是R類別裡面的嵌套類別,R裡面的資源都有分門別類的收好,依照id、layout、drawable...等。
所以整個引數可以白話理解為:到R總管那邊,有一區放layout的資源中,找一個叫做activity_main的佈局檔。
Android文檔:R

AndroidManifest

一開始有介紹過Manifest這邊也是使用xml的格式編寫,是關於應用程式的各種配置和請求權限。看看IDE又幫我們處理了什麼?

所有的Activity都該在這裡註冊在案,要登記在裡,IDE又很貼心的在我們每次創建一個新的Activity時都會這樣幫我們登記好(紅框處),不過我們也要記得查看一下IDE有沒有忘記喔。

  • android:name = ".MainActivity" , .MainActivity是縮寫,前面還要加上manifest 起始標籤中的package名稱才是完整的名稱。
  • android:exported = "true" ,是可以被其他應用程式開啟的意思,若這個activity不希望讓外部應用程式知道的話就將其設置"false",如果該activity是launch頁面的話,這屬性一定要是true。
  • intent-filter : 意圖過濾器中可以放3種標籤,除了上圖的action、category外還有data。
    • action : 聲明接受的意圖操作。
    • category : 聲明接受的意圖類別
    • data : 聲明接受的數據類型,使用一個或多個屬性來指定數據, URI ( scheme, host, port, path) 和 MIME 類型。
  • 上圖中兩個標籤action跟category那兩行就是表示這個activity是launch頁面。

光聽介紹是不是想睡了,底下動手來試試看設置一個Toast吧

Toast

Toast是用來顯示簡短訊息給用戶的泡泡訊息框。來實作一個按下按鈕會跳出Toast訊息的功能吧!
首先先在layout中拉好一個Button元件,要用哪一種layout都可以,屬性自訂,主要是要記得給它id。

<Button
        android:id="@+id/btnToast"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Toast"
        android:textSize="24sp"
        ... /> 

接著在MainAcity中初始化這個Button

  • findViewById()這個函數是為我們取得在佈局檔中定義的元件,我們傳入R.id.btnToast來得到按鈕的實例,findViewById()會返回一個繼承自View的泛型對象。
class MainActivity : AppCompatActivity() {
    //延遲初始化
    lateinit var btnToast: Button
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //Button 初始化
        btnToast = findViewById(R.id.btnToast)
    }
}

接著初始化底下,再為btnToast設置onClickListener
makeText函數需要三個參數:
1. Context類別的對象,Activity本身就是,所以直接使用this引用。
2. 給用戶看的訊息,可以直接字串也可以傳入字串類型變數
3. 訊息顯示的時間長:有LENGTH_SHORT跟LENGTH_LONG兩種參數可選
最後呼叫show()使訊息框可以顯示出來,這樣就設好一個Toast了

btnToast.setOnClickListener { 
    Toast.makeText(this , "Hi,Toast is here!",Toast.LENGTH_SHORT).show()
}

改變Toast顯示的位置

預設為顯示在螢幕底部

binding.btnToast.setOnClickListener {
    val messageToast = "這是1/2高的Toast"
     //取得螢幕高度
    val height = resources.displayMetrics.heightPixels
    val toast = Toast.makeText(this, messageToast, Toast.LENGTH_SHORT)
    //設定顯示在螢幕高度1/2處
    toast.setGravity(Gravity.TOP, 0, height / 2)
    toast.show()
 }

View binding

上面findViewById()這樣一個個綁定元件其實有些麻煩,1個元件就要用2行,元件一多,光是初始化這些元件的程式碼,就會佔掉很大的篇幅。
後來Jetpack推出一個View Binding的功能,讓我們可以替換掉findViewById(),設置方法如下:

build.gradle(module)

module等級的gradle文件中,底下所示的區塊中,將 viewBinding build 設置為 true。加入後記得點及右上角的「sync now

android {
    ...
    buildFeatures {
        viewBinding = true
    }
}

Activity中初始化binding

接著到activity的檔案中的 onCreate()方法中執行以下步驟:

  1. inflate()調用生成的綁定類中包含的靜態方法。這將為要使用的activity創建一個綁定類的實例。
  2. getRoot()通過調用方法或使用 Kotlin 屬性語法(.root)獲取對根視圖的引用。
  3. 傳遞根視圖以 setContentView() 使其成為屏幕上的活動視圖。
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater) //步驟1
        setContentView(binding.root)        //步驟2、3
        ...    
    }
}

使用view binding後MainActivity完整程式碼:

元件記得要給id,才能使用view binding喔!

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
//刪  lateinit var btnToast: Button
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
//刪    btnToast = findViewById(R.id.btnToast)
        val messageToast = "這是1/2高的Toast"  //使用變數傳入Toast
        binding.btnToast.setOnClickListener {
            Toast.makeText(this, messageToast, Toast.LENGTH_SHORT).show()
        }
    }
}    

用Snackbar取代Toast

Snackbar既然是用來取代Toast的,表示兩者功能上有重疊的地方。兩者都是傳遞短訊息的視窗。下表為他們呈現上的差異:

Toast Snackbar
可以客製化決定顯示位置 只能顯示在螢幕底部
不能設置action button 可以設置1個以上action button
只能等Toastg時間到自己消失 在Snackbar消失前就可以將Snackbar滑掉(需在CoordinatorLayout)

Snackbar 語法

跟Toast比起來是Android比較推薦使用在前台顯示短訊息的方式,因為符合Material Design的規範,語法跟Toast非常相似,只差在第一個參數要求的類型不同,並且增加了可以與使用者互動的action button:

  • contextView : 父層view
  • R.string.text_label : 顯示的字串
  • duration 持續顯示的時長 :
    • Snackbar.LENGTH_LONG
    • Snackbar.LENGTH_SHORT
    • Snackbar.LENGTH_INDEFINITE :在被關閉或被另外一條Snackbar覆蓋前都不會消失。
/* 
The view used to make the snackbar.
This should be contained within the view hierarchy you want to display the
snackbar. 
Generally it can be the view that was interacted with to trigger
the snackbar, such as a button that was clicked, or a card that was swiped.
*/
    
val contextView = findViewById<View>(R.id.context_view)

Snackbar.make(contextView, R.string.text_label, Snackbar.LENGTH_SHORT)
    .show()

範例

  1. 增加一個新按鈕
    在activity_main.xml佈局中多拉一個按鈕,等下用來觸發Snackbar:
 <Button
        android:id="@+id/btnSnackbar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Delete"
        android:textSize="24sp"
        .../>
  1. MainActivity中設置setOnClickListener監聽按鈕事件來觸發Snackbar:
    1. 簡單版:
    binding.btnSnackbar.setOnClickListener { view ->
    Snackbar.make(contextView, message, Snackbar.LENGTH_SHORT).Show()
    }
    
    2.加上action button
    • setActionTextColor(Color.YELLOW) :設置文字顏色
    • setAction("Undo"){...} : "Undo"為action button的text,後面lambda則是要再設置一個監聽器做出action被點擊後的行為。
    val contextView = binding.constraintLayout  //父層View
    
    binding.btnSnackbar.setOnClickListener { view ->
            val snackbar = Snackbar.make(contextView, message, Snackbar.LENGTH_INDEFINITE)        
            snackbar.setActionTextColor(Color.YELLOW)
    
            //因為lambda表達式是setAction最後一個參數,所以可以移到()外
            snackbar.setAction("Undo") {
                Snackbar.make(contextView, messageUndo, Snackbar.LENGTH_SHORT).show()
            }.show()
    

完整程式碼

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        //父層View
        val contextView = binding.constraintLayout
        //要顯示的訊息
        val messageToast = "這是1/2高的Toast"
        val messageSb = "項目已刪除"
        val messageSbUndo = "項目已恢復"

        //Toast Button
        binding.btnToast.setOnClickListener {
            val height = resources.displayMetrics.heightPixels
            val toast = Toast.makeText(this,messageToast,Toast.LENGTH_LONG)
            toast.setGravity(Gravity.TOP, 0, height / 2)
            toast.show()
        }
        //Snackbar Button
        binding.btnSnackbar.setOnClickListener { view ->
            val snackbar = Snackbar.make(contextView, messageSb, Snackbar.LENGTH_INDEFINITE)
            snackbar.setActionTextColor(Color.YELLOW)
            //因為lambda表達式是setAction最後一個參數,所以可以移到()外
            snackbar.setAction("Undo") {
                Snackbar.make(contextView, messageSbUndo, Snackbar.LENGTH_SHORT).show()
            }.show()

        }
    }
}


轉成gif檔後UNDO黃色就不見了...

參考
官方文檔 Toast
官方文檔 Build and display a pop-up message
Material Design - Snackbars

明天見


上一篇
第27天 Kotlin小學堂(16) : 抽象類別與介面
下一篇
第29天 單一個 Activity 生命週期介紹
系列文
新手向Android&Kotlin學習紀錄30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言